# coding=utf-8
import tornado.ioloop
import tornado.web
from MyOrm import Orm
from concurrent.futures import ThreadPoolExecutor
from tornado.concurrent import run_on_executor
import datetime, json
import time
import Setting

def roll_back(data, name):
    """Dirty data rollback"""
    o = Orm()
    if name == 'date_log':
        for item in data:
            o.table(name='data', method='delete', data={"date": item[1]})
            o.table(name='date_log', method='update', data=({'id':item[0]}, {"status": 'wait', 'e_num': item[3]+1}))
        o.close()
        print('rollback success')
    else:
        for item in data:
            o.table(name='news_log', method='update', data=({'id':item[0]}, {"status": 'wait', 'e_num': item[5]+1}))
        o.close()
        print('rollback success')


def dateRange(start, end, step=1, format="%Y-%m-%d"):
    strptime, strftime = datetime.datetime.strptime, datetime.datetime.strftime
    days = (strptime(end, format) - strptime(start, format)).days
    return [strftime(strptime(start, format) + datetime.timedelta(i), format) for i in range(0, days, step)]


def put_date_log(start='2013-05-28', end='2018-05-28'):
    o = Orm()
    dates = dateRange(start, end)
    for date in dates:
        item = o.table(name='date_log', method='get', data={'date': date}, method2='one')
        if not item:
            o.table(name='date_log', method='put', data={'date': date, 'status':'wait', 'e_num': 0})
            print('添加新任务:', date)
    o.close()

def put_news_log():
    o = Orm()
    item = o.sql(sql="""select id, url from data""")
    print('generate news_log')
    index = 0
    count = len(item)
    for i in item:
        if o.table(name='news_log', method='get', data={'data_id': i[0]}, method2='one'):
            print('任务存在:', i)
            continue
        o.table(name='news_log', method='put', data={'data_id':i[0], 'url':i[1], 'status':'wait', 'e_num':0})
        index +=1
        if index % 1000 == 0:
            print('add news_log schedule:', index, 'all:', count)
    print('add news_log end')
    o.close()


class MainHandler(tornado.web.RequestHandler):
    executor = ThreadPoolExecutor(1) #同一时间只能有一个人去分配修改任务状态
    @tornado.gen.coroutine
    def get(self):
        s = yield self.call_back()
        self.write(s)
    @run_on_executor
    def call_back(self):
        o = Orm()
        item = o.table(name='date_log', method='get', data={'status': 'doing'}, method2='all')
        doing_count = len(item)
        item = o.table(name='date_log', method='get', data={'status': 'wait'}, method2='all')
        wait_count = len(item)
        item = o.table(name='date_log', method='get', data={'status': 'end'}, method2='all')
        end_count = len(item)
        s = "<span>任务:date_log, doing:%d, wait:%d, end:%d</span><br>"%(doing_count, wait_count, end_count)

        item = o.sql(sql="""select count(id) from news_log where status='doing'""")
        ndoing_count = item[0][0]
        item = o.sql(sql="""select count(id) from news_log where status='wait'""")
        nwait_count = item[0][0]
        item = o.sql(sql="""select count(id) from news_log where status='end'""")
        nend_count = item[0][0]
        s += "<span>任务:news_log, doing:%d, wait:%d, end:%d</span><br>"%(ndoing_count, nwait_count, nend_count)
        s += "<a href='/sina/view/doing'>查看正在执行任务详细信息</a>"
        o.close()
        return s

class SinaViewDoing(tornado.web.RequestHandler):
    executor = ThreadPoolExecutor(1) #同一时间只能有一个人去分配修改任务状态
    @tornado.gen.coroutine
    def get(self):
        s = yield self.call_back()
        self.write(s)

    @run_on_executor
    def call_back(self):
        o = Orm()
        s = str()
        item = o.table(name='date_log', method='get', data={'status': 'doing'}, method2='all')
        for _ in item:
            s += "<span>%s</span><br>"%(str(_))
        item = o.table(name='news_log', method='get', data={'status': 'doing'}, method2='all')
        for _ in item:
            st = time.localtime(_[4])
            date = time.strftime('%Y-%m-%d %H:%M:%S', st)
            count = time.time() - _[4]
            s += "<span>start:%s, runtime: %d, %s</span><br>"%(date, count, str(_))
        o.close()
        if s == "":
            s = '没有正在执行的任务'
        return s

class SinaCallBack(tornado.web.RequestHandler):
    """
    SINA任务执行返回模块
    """
    executor = ThreadPoolExecutor(1) #同一时间只能有一个人去分配修改任务状态
    def get(self):
        self.write("Hello, world")
    @tornado.gen.coroutine
    def post(self):
        self.finish("Hello, world")
        yield self.worker()
    @run_on_executor
    def worker(self):
        """
        get sina result 
        """
        result = self.get_argument('result', 'Hello')
        result = json.loads(result)
        status = result['status']
        item=result['item']
        if status == 'error':
            print('One task is failure, detail:', result)
            self.error_flow(name=result['type'], item=item, data=result['data'])
            return
        o = Orm()
        if result['type'] == 'date_log':
            for info in result['data']:
                # Prevent duplication of tasks
                if o.table(name='data', method='get', data={'date':info['date'], 'url':info['url']}, method2='one'):
                    continue
                else:o.table(name='data', method='put', data=info)
            o.table(name='date_log', method='update', data=({'id':item[0]}, {"status": 'end'}))
            o.close()
            return   
        elif result['type'] == 'news_log':
            o.table(name='data', method='update', data=({'id': item[1]}, result['data']))
            o.table(name='news_log', method='update', data=({'id':item[0]}, {"status": 'end'}))
            o.close()
            return   
    def error_flow(self, name, item, data=False):
        """Dirty data rollback"""
        o = Orm()
        if name == 'date_log':
            o.table(name='date_log', method='update', data=({'id':item[0]}, {"status": 'doing', 'e_num': item[3]+1}))
            o.close()
        else:
            if data == '页面没有找到':
                o.table(name='data', method='delete', data={"id": item[1]})
                o.table(name='news_log', method='delete', data={"id": item[0]})
            else:
                o.table(name='news_log', method='update', data=({'id':item[0]}, {"status": 'doing', 'e_num': item[5]+1}))
            o.close()


class SinaGetTask(tornado.web.RequestHandler):
    """
    SINA任务获取核心模块,负责了任务总调度
    """
    executor = ThreadPoolExecutor(1) #同一时间只能有一个人去分配修改任务状态
    @tornado.gen.coroutine
    def get(self):
        task = yield self.get_task()
        print('发送任务,', task)
        self.finish(json.dumps(task))
    @run_on_executor
    def get_task(self):
        """获取任务"""
        o = Orm()
        while True:
            # judge get date url
            item = o.table(name='date_log', method='get', data={'status':'wait'}, method2='one')
            if item:
                # task type: date_log
                o.table(name='date_log', method='update', data=({'id':item[0]}, {'status':'doing'}))
                o.close()
                return ('date_log', item)
            # if have failure roll back
            item = o.table(name='date_log', method='get', data={'status': 'doing'}, method2='all')
            if item:
                roll_back(item, name='date_log')
                continue
            # judge task table empty
            item = o.sql(sql="""select * from news_log limit 0,1""")
            if not item:
                item = o.sql(sql="""select id, url from data""")
                print('generate news_log')
                index = 0
                count = len(item)
                for i in item:
                    if o.table(name='news_log', method='get', data={'data_id': i[0]}, method2='one'):
                        continue
                    o.table(name='news_log', method='put', data={'data_id':i[0], 'url':i[1], 'status':'wait', 'e_num':0})
                    index +=1
                    if index % 1000 == 0:
                        print('add news_log schedule:', index, 'all:', count)
                print('add news_log end')
            # get news_log
            item = o.table(name='news_log', method='get', data={'status': 'wait'}, method2='one')
            if item:
                # task type: news_log
                o.table(name='news_log', method='update', data=({'id':item[0]}, {'status':'doing', 'start_time':int(time.time())}))
                o.close()
                return ('news_log', item)
            # if have failure roll back
            item = o.table(name='news_log', method='get', data={'status': 'doing'}, method2='all')
            if item:
                roll_back(item, name='news_log')
                continue
            else: 
                o.close()
                print('所有注册任务执行完毕')
                return ('None', '')

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
        (r"/sina/gettask", SinaGetTask),
        (r"/sina/callback", SinaCallBack),
        (r"/sina/view/doing", SinaViewDoing),
        ])

if __name__ == "__main__":
    #put_date_log()
    #put_news_log()
    app = make_app()
    app.listen(Setting.port)
    tornado.ioloop.IOLoop.current().start()
